package org.dromara.common.translation.core.handler;

import cn.hutool.core.util.ObjectUtil;
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.core.JsonStreamContext;
import com.fasterxml.jackson.databind.BeanProperty;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.SerializerProvider;
import com.fasterxml.jackson.databind.ser.ContextualSerializer;
import lombok.extern.slf4j.Slf4j;
import org.dromara.common.core.utils.StringUtils;
import org.dromara.common.core.utils.reflect.ReflectUtils;
import org.dromara.common.translation.annotation.Translation;
import org.dromara.common.translation.core.TranslationInterface;
import org.dromara.common.translation.holder.RequestContext;
import org.dromara.common.translation.holder.RequestContextHolder;

import java.io.IOException;
import java.util.Map;
import java.util.Objects;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 翻译处理器
 * TODO 笔记
 * @作者 Lion Li
 */
@Slf4j
public class TranslationHandler extends JsonSerializer<Object> implements ContextualSerializer {

    public static final Map<String, TranslationInterface> TRANSLATION_MAPPER = new ConcurrentHashMap<>();

    private Translation translation;


    private static Integer listToOneLevel;

    private static Integer oneToListLevel;
    @Override
    public void serialize(Object value, JsonGenerator gen, SerializerProvider serializers) throws IOException {
        RequestContext requestContext = RequestContextHolder.REQUEST_CONTEXT.get();
        if (requestContext == null) {
            throw new IllegalStateException("RequestContext is not available.");
        }

        // 字段名称
        JsonStreamContext jsonContext = gen.getOutputContext();
        if (jsonContext.getParent().inArray() && jsonContext.getParent().getParent().getCurrentName().equals("rows")) {
            requestContext.setIsListRequest(true);
        }
        boolean isListRequest = requestContext.isListRequest();
        Integer jsonNestingLevel = getJsonNestingLevel(jsonContext);
        boolean firstTranslation = requestContext.isFirstTranslation();
        String requestId = requestContext.getRequestId();
        Set<String> context = requestContext.getContextKeys();

        //SAIL 接口url转翻译类型
        String url = requestContext.getUrl().substring(4);
        String translationType = modifyURLToTranslationType(url);

        String mapper = translation.mapper();
        String type = translation.type();
        TranslationInterface trans = TRANSLATION_MAPPER.get(type);

        // SAIL 初始1级
        listToOneLevel = 4;
        // SAIL 初始1级
        oneToListLevel = 4;

        // SAIL活动详情请求
        if(translationType.equals("xhlj_activity_vo")){
            // SAIL 只有5级达到层才能获取会议推荐
            listToOneLevel = 8;
            oneToListLevel = 6;
        }
        // SAIL 活动收藏详情请求
        if(translationType.equals("xhlj_topicActivityCollect_list")){
            listToOneLevel = 6;
        }
        // SAIL 会议聊天请求
        if(translationType.equals("xhlj_activityDiscussion_list")){
            listToOneLevel = 4;
            oneToListLevel = 3;
        }
        // SAIL 会议聊天请求
        if(translationType.equals("xhlj_topic_list")){
            listToOneLevel = 4;
            oneToListLevel = 3;
        }

        if (ObjectUtil.isNotNull(trans)) {
            if (StringUtils.isNotBlank(mapper)) {
                value = ReflectUtils.invokeGetter(gen.getCurrentValue(), mapper);
            }
            if (ObjectUtil.isNull(value)) {
                gen.writeNull();
                return;
            }
            // SAIL 翻译SysUser时
            if(StringUtils.endsWithIgnoreCase(type,"sys_user_id_to_vo")){
                if (isListRequest && jsonNestingLevel > 4) {
                    gen.writeNull();
                    return;
                }else if(!isListRequest && jsonNestingLevel > 3) {
                    gen.writeNull();
                    return;
                }
            }
            // 多对一超过3级3层==大会(0)->活动list(2)->大会(1)
            // SAIL 原因：递归keyMapper会导致低级的多对一翻译出现问题
            if(StringUtils.endsWithIgnoreCase(type,"vo")){
                if (isListRequest && jsonNestingLevel > listToOneLevel) {
                    gen.writeNull();
                    return;
                }else if(!isListRequest && jsonNestingLevel > listToOneLevel - 1) {
                    gen.writeNull();
                    return;
                }
            }
            // 一对多超过3级不翻译 list(2)->list(2) 不设置会导致翻译时间长
            if(StringUtils.endsWithIgnoreCase(type,"list")){
                if (isListRequest && jsonNestingLevel > oneToListLevel) {
                    gen.writeNull();
                    return;
                }else if(!isListRequest && jsonNestingLevel > oneToListLevel - 1) {
                    gen.writeNull();
                    return;
                }
            }
            // SAIL 扩展单条查询为多条
            String keyType = requestId  +":" + type;
            // SAIL activity（一）对象的第一个翻译做标记
            if(firstTranslation){
                requestContext.setIsFirstTranslation(false);
                context.add(keyType);
            }
            // SAIL 如果是activity（二）对象的第一个翻译；进行清空
            if (!firstTranslation && context.contains(keyType)) {
                // 需要确保 jsonContext 和 jsonContext.getParent() 不为 null
                if (jsonContext != null && jsonContext.getParent() != null) {
                    String currentName = jsonContext.getParent().getParent().getCurrentName();
                    if ("rows".equals(currentName)) {
                        context.clear();
                        context.add(keyType);
                    }
                }
            }

            // SAIL 解决查询单条情况
            String keyOneListOneMapper = requestId +":" + mapper + ":" + value;
            // SAIL 解决只翻译大会第一个list问题
            if(context.contains(keyOneListOneMapper) && StringUtils.endsWithIgnoreCase(type,"vo")){
                gen.writeNull();
                return;
            }
            // SAIL 解决list翻译存在重复问题
            String keyListListMapper = requestId + ":" + type+":" + mapper + ":" + value;
            if(context.contains(keyListListMapper) && StringUtils.endsWithIgnoreCase(type,"list")){
                gen.writeNull();
                return;
            }
            try {
                // 解决大会->活动list->大会重复问题 && 保证前三级多对一翻译不出问题
                 if(StringUtils.endsWithIgnoreCase(type,"list")){
                    context.add(keyOneListOneMapper);
                    context.add(keyListListMapper);
                    requestContext.setContextKeys(context);
                }
                log.debug("翻译级别：{}",jsonNestingLevel);
                trans.translation(value, translation, gen);
            } catch (Exception e) {
                gen.writeNull();
                log.error("翻译：{} value：{} 发生异常",type,value, e);
            }finally {
                requestContext.setContextKeys(context);
            }
        } else {
            gen.writeObject(value);
        }
    }

    @Override
    public JsonSerializer<?> createContextual(SerializerProvider prov, BeanProperty property) throws JsonMappingException {
        Translation translation = property.getAnnotation(Translation.class);
        if (Objects.nonNull(translation)) {
            this.translation = translation;
            return this;
        }
        return prov.findValueSerializer(property.getType(), property);
    }

    private String modifyURLToTranslationType(String url) {
        // 删除第一个斜杠
        if (url.startsWith("/")) {
            url = url.substring(1);
        }
        // 删除以数字结尾的部分
        url = url.replaceAll("/\\d+$", "/vo");
        // 替换以任意前缀+list结尾的部分为list
        url = url.replaceAll("([^/]*)list$", "list");
        // 将所有斜杠替换为下划线
        url = url.replace("/", "_");
        return url;
    }

    private  Integer getJsonNestingLevel(JsonStreamContext context) {
        Integer level = 0;
        while (context != null) {
            level++;
            context = context.getParent();
        }
        return level;
    }
}

